/**
 * 
 */
package database.berkeleydb.perf;

import java.io.File;

import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.JEVersion;
import com.sleepycat.persist.*;


/**
 * @author LionBule
 * 2011-5-10 ����03:00:47
 */
public class MyExample {
	/* Global settings */
    protected static final int EXIT_SUCCESS = 0;
    protected static final int EXIT_FAILURE = 1;
    /* Variables */
    private com.sleepycat.je.Environment env;
    private Database db;
    private EntityStore store;
    private PrimaryIndex primaryIndex;
    TestConfig testConfig;

    public MyExample(String args[]) {
        testConfig = new TestConfig(args);
    }

    public static void main(String[] args) {
        MyExample example = new MyExample(args);
        example.execute();
    }

    /**
     * Print the usage.
     */
    public static void usage(String msg) {
        String usageStr;
        if (msg != null) {
            System.err.println(msg);
        }
        usageStr = "Usage: java MyExamplen"
                + " [-h ] [-preload] [-dirtyRead]"
                + " [-useTxns] [-syncCommit]"
                + " [-deferredWrite]n"
                + " [-numThreads ]n"
                + " [-itemsPerTxn ]n"
                + " [-cacheSize ]n"
                + " [-logFileSize ]n"
                + " [-numOperations ]n"
                + " [-operationType ]";
        System.err.println(usageStr);
    }

    /* Set up the test environment. */
    public void setup() throws Exception {
        /* Create a new, transactional database environment. */
        EnvironmentConfig envConfig = new EnvironmentConfig();
        envConfig.setAllowCreate(true);
        envConfig.setTransactional(testConfig.useTxns);
        envConfig.setTxnNoSync(!testConfig.syncCommit);
        envConfig.setTxnWriteNoSync(!testConfig.syncCommit);
        envConfig.setCacheSize(testConfig.cacheSize);
        envConfig.setConfigParam("je.log.fileMax",
                String.valueOf(testConfig.logFileSize));

        env = new Environment(new File(testConfig.envHome), envConfig);

        /* Open the entity store. */
        StoreConfig storeConfig = new StoreConfig();
        storeConfig.setAllowCreate(true);
        storeConfig.setTransactional(testConfig.useTxns);
        storeConfig.setDeferredWrite(testConfig.deferredWrite);

        store = new EntityStore(env, "thead", storeConfig);
        primaryIndex = store.getPrimaryIndex(CompositeKey.class, BasicEntity.class);
        primaryIndex.sortedMap();
        
        /* Preload the database contents into cache. */
        if (testConfig.preload)
            preload();
    }

    /*
     * If the "-preload" flag is set, do a scan to bring the database into
     * memory first.
     */
    public void preload() throws Exception {

        EntityCursor entities = primaryIndex.entities();
        try {
            //for (BasicEntity e : entities) {}
        } finally {
            entities.close();
        }
    }

    /* The execution method. */
    public void execute() {
        try {
            setup();

            /*
             * Fork off the threads to perform the transactions.
             */
            MyThread[] threads = new MyThread[testConfig.numThreads];
            for (int i = 0; i < testConfig.numThreads; i++) {
                threads[i] = createThread(i, store, primaryIndex, testConfig,
                        (testConfig.numOperations / testConfig.numThreads));
            }

            /* Start all threads here. */
            for (int i = 0; i < testConfig.numThreads; i++) {
                threads[i].start();
            }
            /* Wait for them to finish. */
            for (int i = 0; i < testConfig.numThreads; i++) {
                try {
                    threads[i].join();
                } catch (InterruptedException IE) {
                    System.err
                            .println("caught unexpected InterruptedException");
                    System.exit(EXIT_FAILURE);
                }
            }

            /* Close the database and environment. */
            close();
        } catch (Exception e) {
            System.err.println("TestJEDB: " + e);
            e.printStackTrace();
            System.exit(EXIT_FAILURE);
        }
    }

    public MyThread createThread(int threadId,
                                 EntityStore store,
                                 PrimaryIndex pIndex,
                                 TestConfig testConfig,
                                 int numOps) {

        return new MyThread(threadId, store, pIndex, testConfig, numOps);
    }

    public void close() throws Exception {

        try {
            if (store != null) {
                store.close();
            }

            if (env != null) {
                env.close();
            }
        } catch (DatabaseException DE) {
            System.err.println("Caught " + DE + " while closing env and store");
        }
    }

    /**
     * Parses and contains all test properties.
     */
    static class TestConfig {
        /* Use dirty reads. */
        static boolean dirtyRead = false;
        /* Preload records into memory by doing a scan before the test. */
        static boolean preload = false;
        /* Use the deferred write mode to speed up. */
        static boolean deferredWrite = false;
        /* Use synchronous commit. */
        static boolean syncCommit = false;
        /* Use transactions. */
        static boolean useTxns = false;
        /* Number of threads to use. */
        static int numThreads = 1;
        /* Number of items accessed per txn. */
        static int itemsPerTxn = 50;
        /* Cache size, default is 100M. */
        static long cacheSize = (200 << 20);
        /* JE's Log file size, default is 10M. */
        static long logFileSize = (10 << 20);
        /* Environment home. */
        static String envHome = "./tmp";

        /* Operations per test phase. */
        // static int numOperations = (1 << 20); // default to 1 million.
        static int numOperations = 1000000; // default to 1 million.
        /* Operation types include: INSERT, READ, SCAN, DELETE and UPDATE */
        static String operationType = "READ";
        /* Save command-line input arguments. */
        static StringBuffer inputArgs = new StringBuffer();

        private TestConfig(String args[]) {

            if (args.length < 2) {
                usage(null);
                System.exit(EXIT_FAILURE);
            }

            try {
                /* Parse command-line input arguments. */
                for (int i = 0; i < args.length; i++) {
                    String arg = args[i];
                    boolean moreArgs = i < args.length - 1;
                    if (arg.equals("-h") && moreArgs) {
                        envHome = args[++i];
                    } else if (arg.equals("-dirtyRead")) {
                        dirtyRead = true;
                    } else if (arg.equals("-preload")) {
                        preload = true;
                    } else if (arg.equals("-deferredWrite")) {
                        deferredWrite = true;
                    } else if (arg.equals("-syncCommit")) {
                        syncCommit = true;
                    } else if (arg.equals("-useTxns")) {
                        useTxns = true;
                    } else if (arg.equals("-numThreads") && moreArgs) {
                        numThreads = Integer.parseInt(args[++i]);
                    } else if (arg.equals("-itemsPerTxn") && moreArgs) {
                        itemsPerTxn = Integer.parseInt(args[++i]);
                    } else if (arg.equals("-cacheSize") && moreArgs) {
                        cacheSize = Long.parseLong(args[++i]);
                    } else if (arg.equals("-logFileSize") && moreArgs) {
                        logFileSize = Long.parseLong(args[++i]);
                    } else if (arg.equals("-numOperations") && moreArgs) {
                        numOperations = Integer.parseInt(args[++i]);
                    } else if (arg.equals("-operationType") && moreArgs) {
                        operationType = args[++i];
                    } else if (arg.equals("-help")) {
                        usage(null);
                        System.exit(EXIT_SUCCESS);
                    } else {
                        usage("Unknown arg: " + arg);
                        System.exit(EXIT_FAILURE);
                    }
                }

                /* Save command-line input arguments. */
                for (String s : args) {
                    inputArgs.append(" " + s);
                }
                inputArgs.append(" je.version=" + JEVersion.CURRENT_VERSION);
                System.out.println("nCommand-line input arguments:n "
                        + inputArgs);
                System.out.println("Test configurations:nt" + this + "n");
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(EXIT_FAILURE);
            }
        }

        @Override
        public String toString() {
            return "TestConfig={ " +
                   " dirtyRead=" + dirtyRead +
                   " preload=" + preload +
                   " deferredWrite=" + deferredWrite +
                   " syncCommit=" + syncCommit +
                   " useTxns=" + useTxns +
                   " numberThreads=" + numThreads +
                   " numOperations=" + numOperations +
                   " operationType=" + operationType +                   " itemsPerTxn=" + itemsPerTxn +
                   " logFileSize=" + logFileSize +
                   " cacheSize=" + cacheSize +
                   " envHome=" + envHome + " }";
        }
    }	
}
